using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims;
using System.Text;
using CampusModel;
using CampusModel.Enum;
using CampusModel.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using PublicProject.PublicClass.Basics;
using PublicProject.PublicClass.ExtensionException;

namespace CampusDataOperationLibrary;

public class LoginOperation(CampusModelDbContext dbContext)
{
    public Task<List<Class>> GetClassesAsync()
    {
        return dbContext.Classes.ToListAsync();
    }
    public async Task<string> RegisterUser(string username, string password, UserRole role, string? studentClassName)
    {
        // 注册用户并获取用户实体
        var user = await RegisterUserInternalAsync(username, password, role, studentClassName);

        // 创建声明并生成令牌
        var claims = CreateClaims(user, role.ToString());
        return this.BuildToken(claims, new JWTOption());
    }

    private async Task<User?> RegisterUserInternalAsync(string username, string password, UserRole role, string? studentClassName = null)
    {
        if (role == UserRole.Student)
        {
            // 检查用户名是否已被注册
            var students = dbContext.Students.Where(s => s.Name == username);
            if (students.Any())
            {
                throw new ExceptionWithStatus(HttpStatusCode.Conflict, "用户名已被注册");
            }
            var studentClass = new Class();
            if (studentClassName != null)
            {
                // 检查班级是否存在
                var classes = dbContext.Classes.Where(c => c.ClassName == studentClassName);
                if (!classes.Any())
                {
                    throw new ExceptionWithStatus(HttpStatusCode.NotFound, "班级不存在");
                }
                studentClass = classes.First();
            }
            // 注册学生
            var student = new Student(username)
            {
                ClassId = studentClass.Id
            };
            dbContext.Students.Add(student);
            await dbContext.SaveChangesAsync();
            // 创建用户
            var user = new User(role, username, password, student.Id);
            dbContext.Users.Add(user);
            await dbContext.SaveChangesAsync();
            return user;
        }
        else
        {
            // 注册教师
            var teacher = new Teacher(username);
            dbContext.Teachers.Add(teacher);
            await dbContext.SaveChangesAsync();

            // 创建用户
            var user = new User(role, username, password, teacher.Id);
            dbContext.Users.Add(user);
            await dbContext.SaveChangesAsync();
            return user;
        }
    }

    private List<Claim> CreateClaims(User? user, string role)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())
        };
        claims.Add(new Claim(ClaimTypes.Role, role));
        claims.Add(new Claim(ClaimTypes.Name, user.Name));
        return claims;
    }
    public async Task<string> LoginUser(string username, string password)
    {
        // 查找用户
        var user = await FindUserAsync(username, password) ?? throw new ExceptionWithStatus(HttpStatusCode.Unauthorized, "用户名或密码错误");
        // 创建声明并生成令牌
        var claims = CreateClaims(user, user.Role.ToString());
        return this.BuildToken(claims, new JWTOption());
    }

    private async Task<User?> FindUserAsync(string username, string password)
    {
        return await dbContext.Users.SingleOrDefaultAsync(u => u!.Name == username && u.password == password);
    }
    
    private string BuildToken(IEnumerable<Claim> claims, JWTOption options)
    {
        TimeSpan value = TimeSpan.FromSeconds(options.ExpireSeconds);
        SigningCredentials signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.Key)), "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256");
        string issuer = options.Issuer;
        string audience = options.Audience;
        DateTime? expires = DateTime.Now.Add(value);
        SigningCredentials signingCredentials2 = signingCredentials;
        JwtSecurityToken token = new JwtSecurityToken(issuer, audience, claims, null, expires, signingCredentials2);
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}